package com.opc.demo.util;

import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.dcom.list.ClassDetails;
import org.openscada.opc.lib.common.AlreadyConnectedException;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.common.NotConnectedException;
import org.openscada.opc.lib.da.*;
import org.openscada.opc.lib.list.Categories;
import org.openscada.opc.lib.list.Category;
import org.openscada.opc.lib.list.ServerList;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.Executors;

@Service
public class OPCHelper {

	@Value("${opc.host}")
	private  String host;

	@Value("${opc.user}")
	private  String user;

	@Value("${opc.password}")
	private  String password;

	@Value("${opc.domain}")
	private  String domain;

	@Value("${opc.clsid}")
	private  String clsid;

	@Value("${opc.progId}")
	private  String progId;

	@Value("${opc.read_device:0}")
	private  String readDevice;

	public List<Map<String, Object>> showAllOPCServer() {
		return showAllOPCServer(host, user, password, domain);
	}

	public List<Map<String, Object>> showAllOPCServer(String host, String user, String password, String domain) {
		List<Map<String, Object>> list = new ArrayList<>();
		try {
			ServerList serverList = new ServerList(host, user, password, domain);
			// 支持DA 1.0，DA 2.0规范
			Collection<ClassDetails> detailsList = serverList.listServersWithDetails(
					new Category[] { Categories.OPCDAServer10, Categories.OPCDAServer20 }, new Category[] {});
			for (final ClassDetails details : detailsList) {
				System.out.println("ClsId=" + details.getClsId() + " ProgId=" + details.getProgId() + " Description="
						+ details.getDescription());
				Map<String, Object> map = new HashMap<>();
				map.put("ClsId", details.getClsId());
				map.put("ProgId", details.getProgId());
				list.add(map);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	public synchronized Server Connect() {
		final ConnectionInformation ci = new ConnectionInformation();
		ci.setHost(host);
		ci.setUser(user);
		ci.setPassword(password);
		ci.setDomain(domain);
		if (clsid != null && clsid.length() > 0) {
			ci.setClsid(clsid);
		}
		if (progId != null && progId.length() > 0) {
			ci.setProgId(progId);
		}

		// create a new server
		Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

		try {
			// connect to server
			server.connect();
			return server;
		} catch (final JIException e) {

			System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (AlreadyConnectedException e) {
			e.printStackTrace();
		}
		return null;
	}

	private Object getValue(JIVariant value) {
		try {
			switch (value.getType()) {
			case JIVariant.VT_EMPTY:	return null;
			case JIVariant.VT_NULL:		return null;
			case JIVariant.VT_I1:		return value.getObjectAsChar();
			case JIVariant.VT_I2:		return value.getObjectAsShort();
			case JIVariant.VT_I4:		return value.getObjectAsInt();
			case JIVariant.VT_I8:		return value.getObjectAsLong();
			case JIVariant.VT_UI2:		return value.getObjectAsUnsigned().getValue();
			case JIVariant.VT_UI4:		return value.getObjectAsUnsigned().getValue();
			case JIVariant.VT_BOOL:		return value.getObjectAsBoolean();
			case JIVariant.VT_R4:		return value.getObjectAsFloat();
			case JIVariant.VT_R8:		return value.getObjectAsDouble();
			case JIVariant.VT_BSTR:		return value.getObjectAsString();
			case JIVariant.VT_DECIMAL:	return value.getObjectAsDouble();
			default :
				System.out.println("value type undef "+value.getType());
				System.out.println("value is "+value);
			}
		} catch (JIException e) {
			e.printStackTrace();
		}
		return null;
	}

	public Map<String, Object> readTags(List<String> tags) {
		Map<String, Object> retMap = new HashMap<String, Object>();
		Server server = Connect();
		if (server == null)
			return retMap;
		try {
			System.out.println("=================================================================:begin");
			Group group = server.addGroup("test");
			for (String tag : tags) {
				try{
					Item item = group.addItem(tag);
					Object value = null;
					//失败读三次。
					for(int t = 0 ; value==null && t<=10 ; t ++){
						ItemState itemState = item.read("1".equals(readDevice));
						value = getValue(itemState.getValue());
						System.out.println("第"+t+"次抄读:"+tag+"="+value);
						if(value==null){
							Thread.sleep(200);
						}
					}
					retMap.put(tag, value);
				}catch(Exception e){
					System.out.println("Error tag:"+tag);
					e.printStackTrace();
				}
			}
			/*List<Item> itemsList = new ArrayList<Item>();
			group.setActive(true);
			for (String tag : tags) {
				try {
					Item item = group.addItem(tag);
				} catch (AddFailedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			Map<Item, ItemState> items = group.read(true);
			for(Item item:items.keySet()){
				retMap.put(item.getId(), getValue(items.get(items).getValue()));
			}*/
			group.clear();
			group.remove();
			System.out.println("=================================================================:end");
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (NotConnectedException e) {
			e.printStackTrace();
		} catch (JIException e) {
			e.printStackTrace();
		} catch (DuplicateGroupException e) {
			e.printStackTrace();
		}
		try{
			server.disconnect();
		}catch(Exception e){
			System.out.println("Error disconnect:");
			e.printStackTrace();
		}
		
		return retMap;

	}

	public static void test(String itemId) {
		// create connection information
		final ConnectionInformation ci = new ConnectionInformation();
		OPCHelper opcHelper = new OPCHelper();

		ci.setHost(opcHelper.host);
		ci.setUser(opcHelper.user);
		ci.setPassword(opcHelper.password);
		ci.setProgId(opcHelper.domain);
		ci.setClsid(opcHelper.clsid);
		//ci.setProgId("KEPServerEX 6.4");
		// ci.setClsid("08a3cc25-5953-47c1-9f81-efe3046f2d8c"); // if ProgId is
		// not working, try it using the Clsid instead
		// final String itemId = "tag1";
		// create a new server
		final Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor());

		try {
			// connect to server
			server.connect();
			// add sync access, poll every 500 ms
			final AccessBase access = new SyncAccess(server, 500);
			access.addItem(itemId, new DataCallback() {
				@Override
				public void changed(Item item, ItemState itemState) {
					// TODO Auto-generated method stub
					try {
						//System.out.println("Resut: " + itemState.toString() + "value:" + itemState.getValue().getObjectAsDouble());
						if (!itemState.getValue().isNull()) {
							System.out.println("Result: " + itemState + ", value:" + itemState.getValue().toString());
						}
						else {
							System.out.println("Result: " + itemState + ", value is null");
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}

			});

			/*
			 * final Group group = server.addGroup("test"); group.read(true,
			 * group) // Add a new item to the group， //
			 * 将一个item加入到组，item名字就是MatrikonOPC
			 * Server或者KEPServer上面建的项的名字比如：u.u.TAG1，PLC.S7-300.TAG1 final Item
			 * item = group.addItem(itemId); item.read(true)
			 */

			access.isActive();

			// start reading
			access.bind();
			// wait a little bit
			Thread.sleep(10 * 1000);
			// stop reading
			access.unbind();

			server.disconnect();

		} catch (final JIException e) {
			System.out.println(String.format("%08X: %s", e.getErrorCode(), server.getErrorMessage(e.getErrorCode())));
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AlreadyConnectedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NotConnectedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (DuplicateGroupException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AddFailedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		//test("KING.Value");
		//test("实验用主变温度.Value");

		testReadTags();
	}

	private static void testReadTags() {
		OPCHelper opc = new OPCHelper();
		List<String> items = new ArrayList<String>();
		items.add("KING.Value");
		items.add("实验用主变温度.Value");
		Map<String, Object> map = opc.readTags(items);
		if (map != null && map.size() > 0) {
			for (String s : map.keySet()) {
				System.out.println(s + ":" + map.get(s));
			}
		}
	}
}
